﻿using System.Collections.Immutable;
using DimensionsHelper.Common.Utils;
using DimensionsHelper.LanguageService.Protocol;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService.Symbols;

/// <summary>
///     本地符号定义。可以是变量或数组。
/// </summary>
public sealed class LocalSymbol : BindableSymbol
{
    private readonly int[]? _boundaries;

    /// <summary>
    ///     创建一个本地变量符号，适用于<see cref="VariableDeclaration" />和<see cref="ArrayDeclaration" />。
    /// </summary>
    /// <param name="decl">声明本地变量的标识符</param>
    /// <param name="bindingType">初始的绑定类型，如果忽略，默认是<see cref="ValueTypeSymbol.Any" /></param>
    /// <param name="containingSymbol">包含此符号的父级符号，一般情况，此参数是包含此本地符号的函数符号</param>
    internal LocalSymbol(VariableDeclaration decl, ITypeSymbol? bindingType = null,
        ITypeSymbol? containingSymbol = null) :
        base(SymbolKinds.Variable, decl, containingSymbol, null)
    {
        Declaration = decl;
        OriginalDefinition = this;
        IsReadOnly = false;

        if (!decl.Is<ArrayDeclaration>(SyntaxKinds.ArrayDeclaration, out ArrayDeclaration? arrDecl))
        {
            UpdateBindingSymbol(decl.Uri, decl.NameIdentifier.Start, 
                bindingType ?? ValueTypeSymbol.Null /* 默认初始值是 Null */);
        }
        else
        {
            Kind = SymbolKinds.Array;
            Rank = arrDecl.Rank;
            _boundaries = new int[arrDecl.BoundariesCount];
            for (int i = 0; i < arrDecl.BoundariesCount; i++)
            {
                if (arrDecl.TryGetBoundary(i + 1, out IntegerLiteral? bound))
                {
                    _boundaries[i] = bound.Value;
                }
                else
                {
                    _boundaries[i] = -1;
                }
            }

            UpdateBindingSymbol(decl.Uri, decl.NameIdentifier.Start,
                bindingType ?? ValueTypeSymbol.Any.MakeArrayType(Rank, _boundaries));
        }
    }


    internal LocalSymbol(ConstantDeclaration declaration) :
        base(SymbolKinds.Constant, declaration, null, null)
    {
        IsReadOnly = true;
        Declaration = declaration;
        OriginalDefinition = this;
        if (!Expression.IsEmptyExpression(declaration.Initializer))
        {
            Initializer = new WeakReference<Expression>(declaration.Initializer);
        }
    }


    internal LocalSymbol(ConstantSignature signature, ITypeSymbol? containingSymbol,
        ModuleSymbol? containingModule) :
        base(SymbolKinds.Constant, signature, containingSymbol,
            containingModule ?? containingSymbol?.ContainingModule as ModuleSymbol)
    {
        IsReadOnly = true;
        Declaration = signature;
        OriginalDefinition = this;
        if (!Expression.IsEmptyExpression(signature.Initializer))
        {
            Initializer = new WeakReference<Expression>(signature.Initializer);
        }
    }

    public override bool IsReadOnly { get; }


    private Dictionary<DocumentUri, TypePositionMap>? PositionTypes { get; set; }

    private WeakReference<Expression>? Initializer { get; }

    public override ISymbol OriginalDefinition { get; }

    public override Declaration Declaration { get; }


    /// <summary>
    ///     数组符号的维度数
    /// </summary>
    public int Rank { get; }

    /// <summary>
    ///     如果此符号是函数返回值符号，返回<see langword="true"/>
    /// </summary>
    public bool IsFunctionReturnSymbol => ContainingSymbol?.Declaration is FunctionDeclaration function &&
                                          function.Name.Equals(Name, StringComparison.OrdinalIgnoreCase);
    

    public override bool CanMakeArray => false;


    public override bool IsConstant =>
        Declaration.IsOneOfNode(SyntaxKinds.ConstantDeclaration, SyntaxKinds.ConstantSignature);


    public override bool IsLocalVariable =>
        Declaration.IsOneOfNode(SyntaxKinds.VariableDeclaration, SyntaxKinds.ArrayDeclaration);


    /// <summary>
    ///     获取符号的数组维度数宽度定义，如果某个维度没有定义宽度，
    ///     将保留负数，或者未定义。
    /// </summary>
    public ImmutableArray<int> GetBoundaries()
    {
        return _boundaries?.ToImmutableArray() ?? ImmutableArray<int>.Empty;
    }


    /// <summary>
    ///     获取指定维度数的数组宽度定义
    /// </summary>
    /// <param name="rank">维度数，1开始</param>
    /// <returns>如果指定维度的数组宽度已定义，返回指定宽度，否则，返回-1</returns>
    public int GetBoundary(int rank)
    {
        if (rank <= 0 || _boundaries == null || _boundaries.Length < rank)
        {
            return -1;
        }

        return _boundaries[rank - 1];
    }


    /// <summary>
    ///     更新指定文件中指定位置的绑定类型
    /// </summary>
    internal void UpdateBindingSymbol(DocumentUri uri, int position, ITypeSymbol symbol)
    {
        PositionTypes ??= new Dictionary<DocumentUri, TypePositionMap>();

        if (!PositionTypes.TryGetValue(uri, out TypePositionMap? map))
        {
            map = new TypePositionMap();
            PositionTypes.Add(uri, map);
        }

        map.AddType(position, symbol);
        UpdateCachedBinding(symbol);
    }


    /// <summary>
    ///     获取指定文件内的最新绑定类型，如果在指定文件中未定义，返回<see langword="null" />
    /// </summary>
    /// <param name="uri">指定文件的URI</param>
    public ITypeSymbol? GetBindingSymbol(DocumentUri uri)
    {
        if (PositionTypes?.TryGetValue(uri, out TypePositionMap? map) == true &&
            map.Types.Count > 0)
        {
            return map.Types[^1].GetTypeSymbol();
        }

        return null;
    }


    /// <summary>
    ///     获取在指定路径文件的指定位置上，当前符号的绑定类型。
    ///     如果指定位置符号未定义，返回<see langword="null" />
    /// </summary>
    /// <param name="uri">指定文件的URI</param>
    /// <param name="position">文件位置</param>
    public ITypeSymbol? GetBindingSymbol(DocumentUri uri, int position)
    {
        return PositionTypes?.TryGetValue(uri, out TypePositionMap? map) == true ? map.FindType(position) : null;
    }


    public override ITypeSymbol GetBindingSymbol()
    {
        if (CachedBinding != null)
        {
            return CachedBinding;
        }

        if (Initializer?.TryGetTarget(out Expression? expr) != true)
        {
            return base.GetBindingSymbol();
        }

        return expr != null && ValueTypeSymbol.TryGetValueSymbol(expr.Kind, out ValueTypeSymbol? valueTypeSymbol)
            ? UpdateCachedBinding(valueTypeSymbol)
            : base.GetBindingSymbol();
    }


    private record TypeCache(int Position, WeakReference<ITypeSymbol> Type)
    {
        public ITypeSymbol? GetTypeSymbol()
        {
            return Type.TryGetTarget(out ITypeSymbol? type) ? type : null;
        }
    }


    private class TypePositionMap
    {
        private bool _isArrayTypeLockAnyType;

        // 需要确保此列表内的Position保持递增
        public List<TypeCache> Types { get; } = [];


        public void AddType(int position, ITypeSymbol typeSymbol)
        {
            // 记录数组类型的绑定类型符号
            if (typeSymbol is { IsArrayType: true })
            {
                ArrayTypeSymbol arrayTypeSymbol = (ArrayTypeSymbol)typeSymbol;
                ArrayTypeSymbol? lastArrayTypeSymbol = null;
                if (Types.Count > 0)
                {
                    lastArrayTypeSymbol = Types[^1].GetTypeSymbol() as ArrayTypeSymbol;
                }

                // 如果最近的类型不是Any，同时添加类型符号不同，锁定为Any
                if (!_isArrayTypeLockAnyType &&
                    lastArrayTypeSymbol != null &&
                    !ReferenceEquals(lastArrayTypeSymbol.OriginalDefinition, typeSymbol.OriginalDefinition) &&
                    !ReferenceEquals(lastArrayTypeSymbol.OriginalDefinition, ValueTypeSymbol.Any))
                {
                    _isArrayTypeLockAnyType = true;
                }

                if (_isArrayTypeLockAnyType &&
                    ReferenceEquals(arrayTypeSymbol.OriginalDefinition, ValueTypeSymbol.Any))
                {
                    typeSymbol = ValueTypeSymbol.Any.MakeArrayType(arrayTypeSymbol.Rank,
                        arrayTypeSymbol.GetBoundaries());
                }
            }

            // 如果和最近的一个符号相同，则不添加符号
            if (Types.Count > 0 && Types[^1].GetTypeSymbol()?.Equals(typeSymbol) == true)
            {
                return;
            }

            if (Types.Count == 0 || Types[^1].Position < position)
            {
                Types.Add(new TypeCache(position, new WeakReference<ITypeSymbol>(typeSymbol)));
            }
            else
            {
                int index = Types.Count - 1;

                while (index >= 0)
                {
                    // 如果位置相同，更新内容
                    if (Types[index].Position == position)
                    {
                        Types[index] = new TypeCache(position, new WeakReference<ITypeSymbol>(typeSymbol));
                        return;
                    }

                    if (Types[index].Position < position)
                    {
                        break;
                    }

                    index--;
                }

                Types.Insert(index, new TypeCache(position, new WeakReference<ITypeSymbol>(typeSymbol)));
            }
        }


        public ITypeSymbol? FindType(int position)
        {
            if (Types.Count == 0 || Types[0].Position > position)
            {
                return null;
            }

            if (Types[^1].Position < position)
            {
                return Types[^1].GetTypeSymbol();
            }

            int index = CollectionHelper.BinarySearch(Types, position,
                (cache, pos) => cache.Position - pos);
            if (index < 0)
            {
                index = ~index - 1;
            }

            return Types[index].GetTypeSymbol();
        }
    }
}